package com.dfrz.utils;


import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import org.springframework.web.multipart.MultipartFile;


import java.io.*;
import java.util.*;
import java.util.concurrent.*;

/**
 * 文件保存至阿里云OSS
 */

public class AliOSSUtil {
    private static final String endpoint = "https://oss-accelerate.aliyuncs.com";
//    private static final String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    private static final String bucketName = "mikufunbucket2";
    private static final String accessKeyId="LTAI5t5dF138zpM8Zccpeg1v";
    private static final String secret="rIF5aupnE0UDFgeToWN86pY9BZbp1V";

    public static String uploadFile(MultipartFile multipartFile) throws Exception{
        //获取上传文件的输入流
        InputStream inputStream = multipartFile.getInputStream();

        //图片上传格式设置
        String fileName = multipartFile.getOriginalFilename();
        String type = fileName.split("\\.")[1];
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(getcontentType("."+type));

        //避免文件覆盖
        fileName = UUID.randomUUID() + fileName;

        // 建立上传实例
        OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,secret);

        //传输加速
        boolean enabled = true;   //true 开启加速
        ossClient.setBucketTransferAcceleration(bucketName, enabled);

        //上传文件到OSS
        ossClient.putObject(bucketName,fileName,inputStream,metadata);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" +bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        //关闭ossClient
        ossClient.shutdown();
        return url;
    }

    //多线程文件分片上传
    public static String upLoadPartFile(MultipartFile multipartFile) throws IOException {
        // 创建一个可重用固定线程数的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(100);

        //图片上传格式设置
        String fileName = multipartFile.getOriginalFilename();
        String type = fileName.split("\\.")[1];

        //避免文件覆盖
        fileName = UUID.randomUUID() + fileName;

        // 初始化分片上传
        OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,secret);
        //传输加速
        boolean enabled = true;   //true 开启加速
        ossClient.setBucketTransferAcceleration(bucketName, enabled);

        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, fileName);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(getcontentType("."+type));
        request.setObjectMetadata(metadata);
        String uploadId = ossClient.initiateMultipartUpload(request).getUploadId();

        // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
        List<PartETag> partETags =  new ArrayList<PartETag>();
        // 每个分片的大小，用于计算文件有多少个分片。单位为字节。
        final long partSize = 1 * 1024 * 1024L;   //1MB。

        // 根据上传的数据大小计算分片数。以本地文件为例，说明如何通过File.length()获取上传数据的大小。
        long fileLength = multipartFile.getSize();
        int partCount = (int) (fileLength / partSize);
        if (fileLength % partSize != 0) {
            partCount++;
        }
        // 遍历分片上传。

        String finalFileName = fileName;
        for (int i = 0; i < partCount; i++) {
            long startPos = i * partSize;
            long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;

            //多线程文件分片上传，解决文件上传效率的问题
            int partNumber = i + 1;
            // 实现并启动线程
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    InputStream inputStream = null;
                    try {
                        inputStream = multipartFile.getInputStream();
                        // 跳过已经上传的分片
                        inputStream.skip(startPos);

                        UploadPartRequest uploadPartRequest = new UploadPartRequest();
                        uploadPartRequest.setBucketName(bucketName);
                        uploadPartRequest.setKey(finalFileName);
                        uploadPartRequest.setUploadId(uploadId);

                        uploadPartRequest.setInputStream(inputStream);
                        // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100 KB。
                        uploadPartRequest.setPartSize(curPartSize);
                        // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
                        uploadPartRequest.setPartNumber(partNumber);
                        // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                        UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                        //每次上传分片之后，OSS的返回结果会包含一个PartETag。PartETag将被保存到PartETags中。
                        synchronized (partETags) {
                            partETags.add(uploadPartResult.getPartETag());
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (inputStream != null) {
                            try {
                                inputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            });
        }
        // 等待所有的分片完成
        // shutdown方法：通知各个任务（Runnable）的运行结束
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            try {
                // 指定的时间内所有的任务都结束的时候，返回true，反之返回false，返回false还有执行完的任务
                executorService.awaitTermination(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
        }
        // 立即关闭所有执行中的线程
        // executorService.shutdownNow();

        // 验证是否所有的分片都完成
        if (partETags.size() != partCount) {
            throw new IllegalStateException("文件的某些部分上传失败！");
        }
        // 完成分片上传 进行排序。partETags必须按分片号升序排列
        Collections.sort(partETags, new Comparator<PartETag>() {
            @Override
            public int compare(PartETag o1, PartETag o2) {
                return o1.getPartNumber() - o2.getPartNumber();
            }
        });

        // 完成分片上传
        CompleteMultipartUploadRequest completeMultipartUploadRequest =
                new CompleteMultipartUploadRequest(bucketName, fileName, uploadId, partETags);
        ossClient.completeMultipartUpload(completeMultipartUploadRequest);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" +bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        //关闭ossClient
        ossClient.shutdown();
        return url;
    }


    public static String getcontentType(String filenameExtension){
        if (".pdf".equalsIgnoreCase(filenameExtension)) {
            return "application/pdf";
        }
        if (".bmp".equalsIgnoreCase(filenameExtension)) {
            return "image/bmp";
        }
        if (".gif".equalsIgnoreCase(filenameExtension)) {
            return "image/gif";
        }
        if (".jpeg".equalsIgnoreCase(filenameExtension) ||
                ".jpg".equalsIgnoreCase(filenameExtension) ||
                ".png".equalsIgnoreCase(filenameExtension)) {
            return "image/jpg";
        }
        if (".html".equalsIgnoreCase(filenameExtension)) {
            return "text/html";
        }
        if (".txt".equalsIgnoreCase(filenameExtension)) {
            return "text/plain";
        }
        if (".vsd".equalsIgnoreCase(filenameExtension)) {
            return "application/vnd.visio";
        }
        if (".pptx".equalsIgnoreCase(filenameExtension) ||
                ".ppt".equalsIgnoreCase(filenameExtension)) {
            return "application/vnd.ms-powerpoint";
        }
        if (".docx".equalsIgnoreCase(filenameExtension)) {
            return "application/msword";
        }
        if (".xml".equalsIgnoreCase(filenameExtension)) {
            return "text/xml";
        }
        if (".mp4".equalsIgnoreCase(filenameExtension)) {
            return "video/mp4";
        }
        return "image/jpg";
    }
}
